package com.pudding.ragdemo;

import com.pudding.tools.ModelUtil;
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.DocumentParser;
import dev.langchain4j.data.document.DocumentSplitter;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.document.parser.TextDocumentParser;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.openai.OpenAiEmbeddingModel;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.redis.RedisEmbeddingStore;

import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

public class MeituanRagLoader {
    public static void main(String[] args) throws URISyntaxException {
        // 1. 读取本地知识库文件
        Path documentPath = Paths.get(MeituanRagLoader.class.getClassLoader().getResource("meituan-questions.txt").toURI());

        // 文档解析器
        DocumentParser documentParser = new TextDocumentParser();
        // 加载文档，通过文档解析器解析
        Document document = FileSystemDocumentLoader.loadDocument(documentPath, documentParser);

        // 2. 把知识文件分割成多个片段，也就是分解成一个个的知识条目
        DocumentSplitter splitter = new MyDocumentSplitter(); // 我们自定义一个分割器
        List<TextSegment> segments = splitter.split(document);

        // 3. 把片段转换成向量（条目进行文本向量化），并保存到向量数据库中（这里使用的redis）
        EmbeddingModel embeddingModel = OpenAiEmbeddingModel.builder()
                .baseUrl(ModelUtil.BASE_URL_OPENAI)
                .apiKey(ModelUtil.API_KEY_OPENAI)
                .build();
        List<Embedding> embeddings = embeddingModel.embedAll(segments).content();
        // 创建向量数据库,并将向量化的片段保存到向量数据库中
        EmbeddingStore<TextSegment> embeddingStore = RedisEmbeddingStore.builder()
                .host("127.0.0.1")
                .port(6379)
                .dimension(1536)
                .indexName("meituan-rag")
                .build();

        /**
         * 向量（embeddings）：用于相似度搜索
         * - 向量是文本的数学表示（1536维的浮点数数组）
         * - 通过计算向量间的余弦相似度来找到相关内容
         * - 人类无法直接阅读，只是用于计算的
         * 原始文本（segments）：用于生成答案
         * - 保存实际的问答内容，如"Q：在线支付取消订单后钱怎么返还？订单取消后..."
         * - 找到相似向量后，需要返回对应的原始文本
         * - 将原始文本作为上下文提供给LLM生成答案
         */
        embeddingStore.addAll(embeddings, segments); // 存储向量，和向量对应的原始文本
    }
}
